#!/usr/bin/env python
# vi: et sw=2
#
# Gnome PulseAudio applet
# Copyright (C) 2009  Harry Karvonen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Author: Harry Karvonen <harry.karvonen@gmail.com>
#         Aki Rautio <aki.rautio@gmail.com>
#

import pygtk
pygtk.require('2.0')

import gtk
import gnomeapplet
import gnome.ui
import os.path
import sys
import gconf

import locale
import gettext

## Global values
APPLET_VERSION = '0.2'
APPLET_DIR = '/usr/share/gnome-pulse-applet'
LOCALE_DIR = '/usr/share/locale'
ICON_DIR = '/usr/share/icons/hicolor/16x16/apps'
CONF_DIR = '/apps/gnome-pulse-applet'
MUTED_ICON = '/usr/share/icons/gnome/scalable/actions/stop.svg'
ICON_THEME = gtk.icon_theme_get_default()

## Localization
locale.setlocale(locale.LC_ALL, '')

gettext.bindtextdomain('gnome-pulse-applet',LOCALE_DIR)
gettext.textdomain('gnome-pulse-applet')

_ = gettext.gettext

locale.bindtextdomain('gnome-pulse-applet',LOCALE_DIR)
locale.textdomain('gnome-pulse-applet')

from GnomePulseAppletInterface import PulseObj, \
                                      PulseSink, \
                                      PulseSinkInfo, \
                                      PulseSinkInputInfo, \
                                      PulseVolume

################################################################################
#
# Config
class Config:

  ##############################################################################
  #
  # Init
  def __init__(self):
    self.client = gconf.client_get_default()
    self.base = CONF_DIR + "/"

    if not self.client.dir_exists(CONF_DIR):
      self.client.add_dir(self.base,gconf.CLIENT_PRELOAD_NONE)
    
    if not self.get("layout") or not self.get("sink"):
      self.__initDefaultConfig()

    return

  ###

  def get(self,data):
    return self.client.get_string(self.base + data)

  ###

  def save(self,data,value):
    self.client.set_string(self.base + data,str(value))
    return

  ###
    
  def __initDefaultConfig(self):
    self.save("layout", "vertical")
    self.save("sink", "0")

    return

################################################################################
#
# PulseApplet
class PulseApplet:

  ##############################################################################
  #
  # Init
  def __init__(self, applet, iid):

    # Variables
    self.about         = None
    self.applet        = applet
    self.dock          = None
    self.icon          = gtk.Image()
    self.pulse         = None
    self.pop           = False
    self.selected_sink = None
    self.tooltips      = gtk.Tooltips()
    self.config        = Config()
    self.panel_size    = 24

    # init
    gnome.init("Pulse Applet", self.applet.version)

    ## Pulse connect
                        # None == localhost
    self.pulse = PulseObj(None, True) # Connects pulseaudio
    #self.pulse = PulseObj("noHostFound", True) # Connects pulseaudio

    # Check if the sink exists
    if int(self.config.get("sink")) >= len(self.pulse.pulse_sink_list()):
      self.config.save("sink", 0)

    if self.pulse.connected:
      self.selected_sink = self.pulse.pulse_sink_list()[int(
                                                      self.config.get("sink"))]

    self.updateIcon()

    ## About window
    self.create_about()
    
    ## Preferences window
    self.create_preferences()
    
    ## Applet icon
    self.applet.add(self.icon)
    self.applet.connect("change-background",
                        self.change_background)
    
    self.applet.connect("size-allocate",
                        self.size_allocate_event)
    self.applet.connect("button-press-event",
                        self.applet_button_press_event)
    self.applet.connect("key-press-event",
                        self.applet_key_press_event)
    self.applet.connect("scroll-event",
                        self.applet_scroll_event)
    self.applet.connect("destroy",
                        self.cleanup, self.applet)
    self.applet.show_all()

    ## Dock
    self.dock = PulseAppletDock(self)
    self.dock.window.connect("button-press-event",
                             self.applet_button_press_event)
    self.dock.window.connect("key-press-event",
                             self.applet_key_press_event)

    return
  
  ###

  def size_allocate_event(self, widget, allocation):
    orient = self.applet.get_orient()

    #print self.panel_size
    #print "a", allocation.height

    #if isinstance(widget, gnomeapplet.Applet):
    #  print "b", widget.get_parent().get_allocation().height
      
    if orient in (gnomeapplet.ORIENT_UP, gnomeapplet.ORIENT_DOWN):
      if self.panel_size == allocation.height:
        return
      self.panel_size = allocation.height
    else:
      if self.panel_size == allocation.width:
        return
      self.panel_size = allocation.width

    #print "size_alloc:", self.panel_size;

    self.updateIcon()

    return
  
  ###

  def change_background(self,panelapplet,backgroundtype,color,pixmap):
    self.applet.modify_bg(gtk.STATE_NORMAL, color)
    self.icon.modify_bg(gtk.STATE_NORMAL, color)
    
    if pixmap is not None:
      s1 = self.applet.get_style()
      s2 = self.applet.get_style()
      
      s1.bg_pixmap[gtk.STATE_NORMAL] = pixmap
      s2.bg_pixmap[gtk.STATE_NORMAL] = pixmap
    
  ###

  def pop_dock(self):
    #print "pop_dock: Called"

    if self.pop:
      self.pop_dock_down()
    else:
      self.pop_dock_up()

    return

  ###

  def pop_dock_up(self):
    #print "pop_dock_up: Called"

    if self.pop:
      return

    if not self.pulse.connected:
      self.pulse.reconnect()

    self.dock.update()
    self.applet.set_state(gtk.STATE_SELECTED)

    self.dock.window.show_all()

    gtk.gdk.keyboard_grab(self.dock.window.window, True)
    gtk.gdk.pointer_grab(self.dock.window.window, True,
        gtk.gdk.BUTTON_PRESS_MASK |
        gtk.gdk.BUTTON_RELEASE_MASK |
        gtk.gdk.POINTER_MOTION_MASK
    )

    self.pop = True

    #print "last", self.dock.last_stream.get_size()
    #print "last", self.dock.last_stream.window.get_size()
    #print "wind", self.dock.window.get_size()
    return

  ###

  def pop_dock_down(self):
    #print "pop_dock_down: Called"

    if not self.pop:
      return

    self.applet.grab_remove()
    self.dock.window.hide_all()

    self.applet.set_state(gtk.STATE_NORMAL)

    gtk.gdk.keyboard_ungrab()
    gtk.gdk.pointer_ungrab()

    self.pop = False

    return

  ###

  def cleanup(self, event, widget):
    #self.pulse.pulse_disconnect()
    del self.applet
    return

  ###

  def updateIcon(self):
    
    #print "updateIcon"
    if self.pulse.connected:
      #self.selected_sink = self.pulse.pulse_sink_list()[0]
      if self.selected_sink.mute == 0:
        self.icon.set_from_pixbuf(self.load_icon(False, self.panel_size-4)) 
        self.tooltips.set_tip(self.icon,_("Master volume") + ": " + 
                str(self.selected_sink.volume.getVolume()) + "%")
      else:
        self.icon.set_from_pixbuf(self.load_icon(True, self.panel_size-4)) 
        self.tooltips.set_tip(self.icon,_("Master volume") + ": " + _("Muted"))
    else:
      self.selected_sink = None
      # FIXME it would be nice have own icon for not connected state
      self.icon.set_from_pixbuf(self.load_icon(False, self.panel_size-4))
      self.tooltips.set_tip(self.icon,_("No connection")) 

  ###
  
  def load_icon(self,isMuted,size):
    if isMuted:
      pixbuf = ICON_THEME.load_icon('audio-volume-muted',
                                    size,
                                    gtk.ICON_LOOKUP_USE_BUILTIN)
    else:
      pixbuf = ICON_THEME.load_icon('audio-volume-medium',
                                    size,
                                    gtk.ICON_LOOKUP_USE_BUILTIN)
    return pixbuf

  ###

  ## Scroll event
  def applet_scroll_event(self, widget, event):
    #print event.type, event.direction, event.state
    if not self.pulse.connected:
      return False

    volume = self.selected_sink.volume

    if event.direction == gtk.gdk.SCROLL_UP or \
       event.direction == gtk.gdk.SCROLL_RIGHT:
      volume.incVolume(10)
    elif event.direction == gtk.gdk.SCROLL_DOWN or \
         event.direction == gtk.gdk.SCROLL_LEFT:
      volume.incVolume(-10)

    self.pulse.pulse_set_sink_volume(self.selected_sink.index, volume)

    self.tooltips.set_tip(self.icon,_("Master volume") + ": " + 
             str(self.selected_sink.volume.getVolume()) + "%")
    #print "New vol:",volume
    return True

  ###

  ## Mouse button actions
  def applet_button_press_event(self, widget, event):

    #print "applet_button_press_event: Called"
    # If we want exit by clicking somewhere
    # FIXME This may be unused
    if event.window != widget.window and \
       event.type == gtk.gdk.BUTTON_PRESS:
      self.pop_dock_down()
      return True

    elif event.window == widget.window:
      if event.button == 3:
        if self.pop:
          self.pop_dock_down()
          return True
        else:
          self.create_menu()
      elif event.button == 2:
        # FIXME This could to better by using updateIcon and ...
        
        if self.selected_sink.mute == 0:
          self.pulse.pulse_mute_sink(self.selected_sink.index)
          self.icon.set_from_pixbuf(self.load_icon(True,self.panel_size-4))
          self.tooltips.set_tip(self.icon,
                                        _("Master volume") + ": " + _("Muted"))
          self.selected_sink.mute = 1
        else:
          self.pulse.pulse_unmute_sink(self.selected_sink.index)
          self.icon.set_from_pixbuf(self.load_icon(False,self.panel_size-4))
          self.tooltips.set_tip(self.icon, _("Master volume") + ": " + 
                str(self.selected_sink.volume.getVolume()) + "%")
          self.selected_sink.mute = 0

      elif event.button == 1:
        self.pop_dock()

        return True

    return False

  ###

  def applet_key_press_event(self, widget, event):
    #print "applet_key_press_event: Called"

    #print widget, event.type
    #print event.keyval
    if not event.type == gtk.gdk.KEY_PRESS:
      return False

    if event.keyval == 32: # Space
      self.pop_dock()
    elif event.keyval == 65293: # Enter
      self.pop_dock()
    elif event.keyval == 65307: # ESC
      self.pop_dock_down()
    #else:
    #  self.pop_dock_down()

    return True

  ###

  ## About Dialog
  def create_about(self, data = None):
    self.about = gtk.AboutDialog()
    self.about.set_name(_('Gnome PulseAudio applet'))
    self.about.set_version(self.applet.version)
    self.about.set_comments(_('Control each PulseAudio audio streams'))
    self.about.set_authors([
        'Harry Karvonen',
        'Aki Rautio'
    ])
    self.about.set_artists(['Petri Partio'])
    self.about.set_website("http://code.google.com/p/gnome-pulse-applet/")
    self.about.connect('response', self.about_response)
    self.about.connect('close', self.about_close)
    return

  def show_about(self,event,data = None):
    self.about.show()
    return
  
  ###
  
  def about_response(self, dialog, response):
    if response < 0:
      dialog.hide()
      dialog.emit_stop_by_name('response')
    return

  ###

  def about_close(self, widget):
    self.about.hide()
    return True

  ###

  ## Preferences window
  def create_preferences(self, data = None):
    conf_layout = self.config.get("layout")
    
    ## Create preferences window
    self.preferences = gtk.Window()
    self.preferences.set_title(_("Pulse Applet Preferences")) 
    self.preferences.set_default_size(500,300)
    self.preferences.set_position(gtk.WIN_POS_CENTER) 
    
    ## Create Alignments and vertical boxes
    prefAlign = gtk.Alignment(0.5,0.5,1,1)
    prefAlign.set_padding(5,5,5,5)
    prefBox = gtk.VBox()
   
    ## Create Tabs
    prefTabs = gtk.Notebook()
    prefTabs.set_property("height-request",250)

    ## Layout tab
    tab_layout = gtk.Alignment(0,0,0,0)
    tab_layout.set_padding(10,10,10,10)
    tab_layout_box = gtk.VBox()

    label_layout_type = gtk.Label("<b>" + _("Applet layout type") + "</b>")
    label_layout_type.set_use_markup(True)
    label_layout_type.set_property("xalign", 0)

    radio_layout_horizontal = gtk.RadioButton(None, _("Horizontal Style"))
    radio_layout_horizontal.connect("toggled", self.change_layout, "horizontal")
    radio_layout_vertical = gtk.RadioButton(radio_layout_horizontal,
                                            _("Vertical Style"))
    radio_layout_vertical.connect("toggled", self.change_layout, "vertical")

    if conf_layout == "vertical":
      radio_layout_vertical.set_active(True)

    tab_layout_box.add(label_layout_type)
    tab_layout_box.add(radio_layout_horizontal)
    tab_layout_box.add(radio_layout_vertical)

    tab_layout.add(tab_layout_box)

    ## Sinks tab
    tab_sinks = gtk.Alignment(0,0,0,0)
    tab_sinks.set_padding(10,10,10,10)
    tab_sinks_box = gtk.VBox()
    label_sink = gtk.Label("<b>" + _("Select sink") + "</b>")
    label_sink.set_use_markup(True)
    label_sink.set_property("xalign",0)
    tab_sinks_box.add(label_sink)

    radio_sink_old = None

    for sink_name in self.pulse.pulse_sink_list():
      radio_sink = radio_sink_old = gtk.RadioButton(radio_sink_old,
                                                    str(sink_name.name))
      radio_sink.set_use_underline(False)
      radio_sink.connect("toggled", self.change_sink, sink_name.index)
      tab_sinks_box.add(radio_sink)

      if sink_name.index == self.selected_sink.index:
        radio_sink.set_active(True)

    tab_sinks.add(tab_sinks_box)

    ## Sources tabs
    tab_icons = gtk.Alignment(0, 0, 0, 0)
    tab_icons.set_padding(10, 10, 10, 10)
    tab_icons_box = gtk.VBox()

    ## FIXME : Get source list

    tab_icons.add(tab_icons_box)
  
    ##  Icons tabs
    tab_icons = gtk.Alignment(0,0,0,0)
    tab_icons.set_padding(10,10,10,10)
    tab_icons_box = gtk.VBox()
    
    ## FIXME : Get streamicon list
    
    
    tab_icons.add(tab_icons_box)
    
    ## Append tabs
    prefTabs.append_page(tab_layout,gtk.Label(_("Layout")))
    prefTabs.append_page(tab_sinks,gtk.Label(_("Sinks")))
    #prefTabs.append_page(tab_sinks,gtk.Label(_("Sources")))
    #prefTabs.append_page(tab_icons,gtk.Label(_("Stream Icons")))
    
    ## Create Close Button
    prefButtons = gtk.Alignment(1, 0.5, 0, 0)
    closeButton = gtk.Button(_("Close"), "gtk-close")
    prefButtons.add(closeButton)
    
    ## Put all together
    prefBox.pack_start(prefTabs,True,True,0)
    prefBox.pack_start(prefButtons,True,True,0)
    prefAlign.add(prefBox)
    self.preferences.add(prefAlign)
    
    ## Make connections
    closeButton.connect("button-press-event", self.preferences_close)
    self.preferences.connect("delete-event", self.preferences_close)
    
    
    return

  ###

  def show_preferences(self, event, data = None):
    self.preferences.show_all()
    return

  ###
  
  def change_layout(self, event,data = None):
    if event.get_active() == True:
      if data == "vertical":
        self.config.save("layout", "vertical")
      elif data == "horizontal":
        self.config.save("layout", "horizontal")

    return
  
  ###
  
  def change_sink(self, event, data = None):
    if event.get_active() == True:
      self.config.save("sink", data)
      self.selected_sink = self.pulse.pulse_sink_list()[data]
  
  ###

  def preferences_response(self, dialog, response):
    if response < 0:
      dialog.hide()
      dialog.emit_stop_by_name('response')
    return

  ###

  def preferences_close(self, widget,event =""):
    self.preferences.hide()
    return True

  ###


  ## Create menu
  def create_menu(self):
    self.verbs = [("about", self.show_about),("preferences", self.show_preferences)]
    self.applet.setup_menu_from_file(APPLET_DIR,"/gnome-pulse-applet.xml",None, self.verbs)
    return

################################################################################
#
# PulseAppletDock

class PulseAppletDock:
  def __init__(self, pulseApplet):

    # Variables
    self.pulseApplet       = pulseApplet
    self.pulseAppletStream = None
    self.streams           = None
    self.window            = gtk.Window(gtk.WINDOW_POPUP)
    self.dockAlignment     = None

    # Init
    if self.pulseApplet.config.get("layout") == "vertical":
      self.pulseAppletStream = PulseAppletCompactStream
    elif self.pulseApplet.config.get("layout") == "horizontal":
      self.pulseAppletStream = PulseAppletHorizontalStream

    if self.pulseAppletStream.vertical:
      self.streams = gtk.HBox(False, 5)
    else:
      self.streams = gtk.VBox(False, 5)

    dockAlignment = gtk.Alignment(0.5, 0.5, 0, 0)
    dockAlignment.set_padding(2, 2, 2, 2)
    dockAlignment.add(self.streams)
    self.dockAlignment = dockAlignment

    dockFrame = gtk.Frame()
    dockFrame.set_shadow_type(gtk.SHADOW_OUT)
    dockFrame.add(dockAlignment)

    self.window.add(dockFrame)
    return

  ###

  def clearStreamsBox(self):
    if self.streams != None:
      self.streams.foreach(self.streams.remove)
    return

  ###

  def addNoConnection(self, streamList):
    l = gtk.Label()
    l.set_markup("<span weight='bold'>" + _('No connection') + "</span>")
    #self.streams.add(l)
    streamList.add(l)

    return

  ###
  
  def addStreamSeparator(self, streamList, vertical_orientation):
    if vertical_orientation:
      streamSeparator = gtk.VSeparator()
    else:
      streamSeparator = gtk.HSeparator()

    streamList.add(streamSeparator)

    return

  ###

  def addStreamObject(self, streamList, pulseSink):
    stream = self.pulseAppletStream(pulseSink)
    #stream = PulseAppletVerticalStream(pulseSink)
    #stream = PulseAppletCompactStream(pulseSink)

    stream.createStreamLayout()

    # This could be
    # stream = PulseAppletSimpleStream(pulseSink,
    #                                  self.stream_button_press_event,
    #                                  self.stream_value_change)
    stream.muteButton.connect("button-press-event",
                              self.stream_button_press_event,
                              pulseSink)

    stream.connect("button-press-event",
                   self.stream_button_press_event,
                   pulseSink)

    stream.volumeAdjust.connect("value-changed",
                                self.stream_value_change,
                                pulseSink)

    #self.streams.add(stream)
    streamList.add(stream)

    #self.last_stream = stream
    return stream.get_size()

  ###
  #
  # Update

  def update(self):
    height = 0
    width = 0
    sinks = []
    streamsAmount = 0

    streamList = None

    if self.pulseApplet.config.get("layout") == "vertical":
      self.pulseAppletStream = PulseAppletCompactStream
    elif self.pulseApplet.config.get("layout") == "horizontal":
      self.pulseAppletStream = PulseAppletHorizontalStream

    if self.pulseApplet.pulse.connected:
      sinks = self.pulseApplet.pulse.pulse_sink_input_list()

      if sinks == None:
        sinks = []

      clients = dict(map(lambda x: (x.index, x),
                     self.pulseApplet.pulse.pulse_client_list()))

      # Set client
      map(lambda x: x.setClient(clients[x.client_id]), sinks)

    sinks.insert(0, self.pulseApplet.selected_sink)

    ## Streams amount, stream names, stream attributes needed
    streamsAmount = len(sinks)

    #self.clearStreamsBox()

    if not self.pulseApplet.pulse.connected:
      streamList = gtk.VBox(False, 5)
      self.addNoConnection(streamList)
      height = 20
      width = 200
    else:
      if self.pulseAppletStream.vertical:
        streamList = gtk.HBox(False, 5)
        width += 1
      else:
        streamList = gtk.VBox(False, 5)
        height += 1

      streamNumber = 1

      for sink in sinks:
        #print sink
        (streamWidth, streamHeight) = self.addStreamObject(streamList, sink)

        #print (streamWidth, streamHeight)
        ## Create Horizontal bar in the end if stream isn't last one
        #if streamNumber < streamsAmount:
        if streamNumber == 1 and streamsAmount > 1:
          self.addStreamSeparator(streamList, self.pulseAppletStream.vertical)
          if self.pulseAppletStream.vertical:
            width += 6
          else:
            height += 6

        streamNumber = streamNumber + 1

        if self.pulseAppletStream.vertical:
          height = streamHeight
          width += streamWidth
        else: 
          height += streamHeight
          width = streamWidth

    # Remove all elements (one only)
    #self.dockAlignment.foreach(self.dockAlignment.remove)
    self.dockAlignment.remove(self.streams)

    self.streams = streamList
    self.dockAlignment.add(streamList)
    #self.dockAlignment.add(self.streams)
    self.window.resize(width, height)
    #print "a", (width, height)

    ##Update position
    self.updatePosition()
    
    return

  ###
  #
  # UpdatePosition

  def updatePosition(self):
    self.window.set_position(gtk.WIN_POS_MOUSE)
    #self.window.set_position(gtk.WIN_POS_NONE)

    # Current position on screen
    (appletX, appletY) = self.pulseApplet.applet.window.get_origin()
    (appletWidth, appletHeight) = self.pulseApplet.applet.window.get_size()
    (dockWidth, dockHeight) = self.window.get_size()

    orient = self.pulseApplet.applet.get_orient()

    if orient == gnomeapplet.ORIENT_UP:
      # CENTER
      #x = appletX - int(dockWidth / 2) + int(appletWidth / 2)
      if (gtk.gdk.Screen().get_width() - dockWidth) < appletX:
        # LEFT
        x = appletX - dockWidth + appletWidth
      else:
        # RIGHT
        x = appletX

      y = appletY - dockHeight

    elif orient == gnomeapplet.ORIENT_RIGHT:
      x = appletX + appletWidth
      # CENTER
      #y = appletY - int(dockHeight / 2) + (appletHeight / 2)
      if (gtk.gdk.Screen().get_height() - dockHeight) > appletY:
        # DOWN
        y = appletY
      else:
        # UP
        y = appletY + appletHeight - dockHeight


    elif orient == gnomeapplet.ORIENT_DOWN:
      # CENTER
      #x = appletX - int(dockWidth / 2) + int(appletWidth / 2)
      if (gtk.gdk.Screen().get_width() - dockWidth) < appletX:
        # LEFT
        x = appletX - dockWidth + appletWidth
      else:
        # RIGHT
        x = appletX

      y = appletY + appletHeight

    else:
      x = appletX - dockWidth
      # CENTER
      #y = appletY - int(dockHeight / 2) + (appletHeight / 2)
      if (gtk.gdk.Screen().get_height() - dockHeight) > appletY:
        # DOWN
        y = appletY
      else:
        # UP
        y = appletY + appletHeight - dockHeight

    self.window.move(x,y)

    return

  ####
  #
  # Events

  ## Button Action
  def stream_button_press_event(self, widget, event, pulseSink):
    button_icon = gtk.Image()

    if isinstance(widget, PulseAppletStream):
      if event.button != 2:
        return True

      button = widget.muteButton
    else:
      button = widget
    
    #print type(pulseSink)

    ## widget.muted tells streams state 
    if pulseSink.mute == 0:
      pulseSink.muteStream(self.pulseApplet.pulse)
    #  button_icon.set_from_icon_name('audio-volume-muted', 36)
    else:
      pulseSink.unmuteStream(self.pulseApplet.pulse)
    #  button_icon.set_from_icon_name('audio-volume-medium', 36)

    button.set_image(self.pulseAppletStream.getIcon(pulseSink.client.name,
                                                    pulseSink.mute))

    if isinstance(pulseSink, PulseSinkInfo):
      # Update icon and icon's tooltip
      self.pulseApplet.updateIcon()

    return True

  ###

  def stream_value_change(self, widget, pulseSink):
    #print "stream_value_change: callback"

    pulseSink.setVolume(self.pulseApplet.pulse,
                        PulseVolume(int(widget.get_value())))

    # FIXME pulseSink.setVolume should identify is given param int or
    # PulseVolume
    #pulseSink.setVolume(self.pulseApplet.pulse, int(widget.get_value()))


    if isinstance(pulseSink, PulseSinkInfo):
      # Update icon and icon's tooltip
      self.pulseApplet.updateIcon()

    return

################################################################################
#
# PulseAppletStream

# This is one stream.
class PulseAppletStream(gtk.EventBox):
  def __init__(self, pulseSink):
    gtk.EventBox.__init__(self)

    # PROVIDES
    self.pulseSink    = pulseSink
    self.muteButton   = None
    self.volumeAdjust = None

  ###

  vertical = None

  ###

  def get_size(self):
    return (-1, -1)

  ###

  # NOTE: Class-method, static-method etc.
  def getIcon(sinkName, isMuted):
    icon = gtk.Image()

    if isMuted:
      icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_BUTTON)
    else:
      icon.set_from_icon_name('audio-volume-medium', gtk.ICON_SIZE_BUTTON)

    return icon

  getIcon = staticmethod(getIcon)

  ###

  def stream_scroll_event(self, widget, event):
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value()

    if event.direction == gtk.gdk.SCROLL_UP or \
       event.direction == gtk.gdk.SCROLL_RIGHT:
      volume += 10
    elif event.direction == gtk.gdk.SCROLL_DOWN or \
         event.direction == gtk.gdk.SCROLL_LEFT:
      volume -= 10

    if volume > 100:
      volume = 100
    elif volume < 0:
      volume = 0

    self.updateAppTooltip(self.pulseSink.client.name, volume) 
    self.volumeAdjust.set_value(volume)

    return True
    
  ###
  
  def stream_increase_volume_event(self, widget, event):
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value() + 10

    if volume > 100:
      volume = 100

    self.updateAppTooltip(self.pulseSink.client.name, volume)
    self.volumeAdjust.set_value(volume)

    return True
    
    
  ###
  
  def stream_decrease_volume_event(self, widget, event):
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value() - 10

    if volume < 0:
      volume = 0

    self.updateAppTooltip(self.pulseSink.client.name, volume)
    self.volumeAdjust.set_value(volume)

    return True

  ###
  
  def stream_slider_changed(self,range,widget,new_value):
    self.updateAppTooltip(self.pulseSink.client.name, new_value)
    self.volumeAdjust.set_value(int(new_value)) 

    return True

################################################################################
#
# PulseAppletHorizontalStream

class PulseAppletHorizontalStream(PulseAppletStream):
  def __init__(self, pulseSink):
    PulseAppletStream.__init__(self, pulseSink)
    return

  ###

  vertical = False

  ###

  def get_size(self):
    return (202, 66)

  ###

  ###

  def createStreamLayout(self):
    vbox = gtk.VBox()
    
    streamName = self.pulseSink.name

    ## Create label for Stream name
    if len(streamName) >= 30:
      streamName = streamName[0:27] + "..."
    
    streamLabel = gtk.Label()
    streamLabel.set_markup("<span size='x-small'>" + streamName + "</span>")
    streamLabel.set_alignment(0,0.5)
    
    ## If Streams client name exits, create label for client name
    if self.pulseSink.client != None:
      streamClientLabel = gtk.Label()
      streamClientLabel.set_markup("<span size='small' weight='bold'>" +
                                   _(self.pulseSink.client.name) + ":</span>")
      streamClientLabel.set_alignment(0,0.5)
      vbox.add(streamClientLabel)
    
    vbox.add(streamLabel)
    
    controls = gtk.HBox()

    ## Create Mute/UnMute button
    muteButton = gtk.Button()
    muteButtonIcon = gtk.Image()
    
    muteButton.set_image(PulseAppletHorizontalStream.getIcon(
                                self.pulseSink.client.name,
                                self.pulseSink.mute)
                         )
    
    controls.add(muteButton)
    
    
    ## Create Volumebar
    volumeLevel = sum(self.pulseSink.volume.values) / \
                  len(self.pulseSink.volume.values)
    volumeAdjust = gtk.Adjustment(volumeLevel, 0, 100, -5)

    streamVolume = gtk.HScale(volumeAdjust)
    streamVolume.set_digits(0)
    streamVolume.set_property("width-request", 160)
    streamVolume.set_value_pos(gtk.POS_RIGHT)
    controls.add(streamVolume)

    self.muteButton = muteButton
    self.volumeAdjust = volumeAdjust
    
    vbox.add(controls)

    self.add(vbox)

    self.connect("scroll-event",
                 self.stream_scroll_event)

    return

  ###

################################################################################
#
# PulseAppletCompactStream

class PulseAppletCompactStream(PulseAppletStream):
  def __init__(self, pulseSink):
    PulseAppletStream.__init__(self, pulseSink)
    return

  ###

  vertical = True

  ###

  def get_size(self):
    return (34, 190)

  ###

  # NOTE: Class-method, static-method etc.
  def getIcon(sinkName, isMuted):
    icon = gtk.Image()

    import re

    if re.search("selected sink", sinkName, re.I):
      if isMuted:
        icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_MENU)
      else:
        icon.set_from_icon_name('audio-volume-high', gtk.ICON_SIZE_MENU)

    elif re.search("rhythmbox", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/rhythmbox.png", isMuted)
    elif re.search("nereid", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/media-player-banshee.png", isMuted)
    elif re.search("xine", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/xine.png", isMuted)
    elif re.search("amarok", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/amarok.png", isMuted)
    elif re.search("pidgin", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/pidgin.png", isMuted)
    elif re.search("vlc", sinkName, re.I):
      icon = PulseAppletCompactStream.createIcon(
                        ICON_DIR + "/vlc.png", isMuted)
    #elif re.search("", sinkName, re.I):
    #  icon = PulseAppletCompactStream.createIcon(
    #                   ICON_DIR + "/.png", isMuted)

    else:
      if isMuted:
        icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_MENU)
      else:
        icon.set_from_icon_name('audio-volume-low', gtk.ICON_SIZE_MENU)
    
    return icon
  
  getIcon = staticmethod(getIcon)

  ###
  
  # NOTE: Class-method, static-method etc.
  def createIcon(sinkFileName, isMuted):
    icon = gtk.Image()
    if isMuted == True:
      iconbuf = gtk.gdk.pixbuf_new_from_file_at_size(sinkFileName,16,16)
      mutedbuf = gtk.gdk.pixbuf_new_from_file_at_size(MUTED_ICON, 8, 8)
      mutedbuf.composite(iconbuf, 8, 8, 8, 8, 8, 8, 1, 1,
                         gtk.gdk.INTERP_NEAREST, 255)
      icon.set_from_pixbuf(iconbuf)
    else:
      icon.set_from_file(sinkFileName)
    return icon
  
  createIcon = staticmethod(createIcon)

  ###
  
  def updateAppTooltip(self, sinkName, volume):
    self.icon_tooltip.set_tip(self.muteButton, sinkName)
    return
 
  ###
    
  def createStreamLayout(self):

    # Layout here
    vbox = gtk.VBox()
    
    
    ## Sink icon with mute volume button
    muteButton = gtk.Button()
    self.icon_tooltip = gtk.Tooltips()
    
    muteButtonIcon = PulseAppletCompactStream.getIcon(
                                self.pulseSink.client.name,
                                self.pulseSink.mute)

    muteButton.set_property("can-focus", False)
    muteButton.set_image(muteButtonIcon)
    muteButton.set_relief(gtk.RELIEF_NONE)

    vbox.pack_start(muteButton, False, False, 2)
    self.muteButton = muteButton
    self.updateAppTooltip(self.pulseSink.client.name,
                          self.pulseSink.volume.getVolume())  
    
    
    ## "Increase volume" button
    ##increaseButton = gtk.Button("+")
    ##increaseButton.set_relief(gtk.RELIEF_NONE)
    ##increaseButton.set_property("can-focus", False)
    #vbox.pack_start(increaseButton, False, False, 0)
    
    ## Volume slider
    volumeLevel = sum(self.pulseSink.volume.values) / \
                  len(self.pulseSink.volume.values)
    volumeAdjust = gtk.Adjustment(volumeLevel, 0, 100, 5)

    streamVolume = gtk.VScale(volumeAdjust)
    streamVolume.set_property("height-request", 150)
    streamVolume.set_draw_value(True)
    streamVolume.set_digits(0)
    streamVolume.set_value_pos(gtk.POS_BOTTOM)
    streamVolume.set_inverted(True)
    vbox.pack_start(streamVolume, True, False, 0)
    
    
    ## "Decrease volume" button
    ##decreaseButton = gtk.Button("-")
    ##decreaseButton.set_relief(gtk.RELIEF_NONE)
    #vbox.pack_start(decreaseButton,False,False,0)


    ## Add these to applet and make needed connections 
    self.volumeAdjust = volumeAdjust
    self.add(vbox)
 
    ##increaseButton.connect("button-press-event",
    ##             self.stream_increase_volume_event)
    
    streamVolume.connect("change-value",self.stream_slider_changed)
    
    ##decreaseButton.connect("button-press-event",
    ##             self.stream_decrease_volume_event)
 
    self.connect("scroll-event",
                 self.stream_scroll_event)

    return

  ###

################################################################################
#
# MAIN

def pulse_applet_factory(applet, iid):
  applet.version = APPLET_VERSION
  PulseApplet(applet, iid)
  return True

if len(sys.argv) == 2 and sys.argv[1] == "run-in-window":
  main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
  main_window.set_title("Pulse Applet")
  main_window.connect("destroy", gtk.main_quit)
  app = gnomeapplet.Applet()
  pulse_applet_factory(app, None)
  app.reparent(main_window)
  main_window.show()
  gtk.main()

else:
  #import sys
  #sys.stdout = open("/tmp/pulse-applet.log", "w")
  #sys.stderr = open("/tmp/pulse-applet.log", "w+")

  gnomeapplet.bonobo_factory("OAFIID:GNOME_PulseApplet", 
                             gnomeapplet.Applet.__gtype__, 
                             "pulse-applet", "0", pulse_applet_factory)
